import * as vscode from 'vscode';
import * as path from 'path';
import { MagicApiClient, MagicServerConfig, MagicGroupMetaRaw } from '../magicApiClient';
import { MAGIC_RESOURCE_TYPES, MagicResourceType } from '../types';
import { getAgentsManual } from '../agentsManual';
import { MirrorMeta, MirrorFileMeta } from './types';
import { iterateZipEntries, ZipYieldItem } from './zipUtils';
import { debug } from '../logger';

/**
 * 镜像同步管理器
 * 使用 ZIP 下载并解析 .ms 与元数据，完成本地镜像初始化/更新
 */
export class MirrorSyncManager {
    private toPosix(rel: string): string { return rel.replace(/\\/g, '/'); }

    private async ensureDir(uri: vscode.Uri): Promise<void> {
        try { await vscode.workspace.fs.stat(uri); } catch { await vscode.workspace.fs.createDirectory(uri); }
    }

    // 依赖注入：使用元数据管理器的实现
    private deps = {
        readMirrorMeta: async (_folder: vscode.Uri): Promise<MirrorMeta | null> => null,
        writeMirrorMeta: async (_root: vscode.Uri, _meta: MirrorMeta): Promise<void> => { throw new Error('writeMirrorMeta 未注入'); },
        toMirrorMetaFromServer: (_info: any): MirrorFileMeta => { throw new Error('toMirrorMetaFromServer 未注入'); },
        writeLocalMeta: async (_root: vscode.Uri, _meta: MirrorFileMeta): Promise<void> => { throw new Error('writeLocalMeta 未注入'); },
        writeLocalGroupMeta: async (_root: vscode.Uri, _type: MagicResourceType, _groupPathSub: string | undefined, _meta: MagicGroupMetaRaw, _opts?: { refreshChildren?: boolean }): Promise<void> => { throw new Error('writeLocalGroupMeta 未注入'); },
        writeLocalScriptAndUpdateMeta: async (_root: vscode.Uri, _type: MagicResourceType, _groupPathSub: string | undefined, _fileName: string, _script: string, _extra?: Partial<MirrorFileMeta>): Promise<void> => { throw new Error('writeLocalScriptAndUpdateMeta 未注入'); },
    };

    public setDependencies(
        readMirrorMeta: (folder: vscode.Uri) => Promise<MirrorMeta | null>,
        writeMirrorMeta: (root: vscode.Uri, meta: MirrorMeta) => Promise<void>,
        toMirrorMetaFromServer: (info: any) => MirrorFileMeta,
        writeLocalMeta: (root: vscode.Uri, meta: MirrorFileMeta) => Promise<void>,
        writeLocalGroupMeta: (root: vscode.Uri, type: MagicResourceType, groupPathSub: string | undefined, meta: MagicGroupMetaRaw, opts?: { refreshChildren?: boolean }) => Promise<void>,
        writeLocalScriptAndUpdateMeta: (root: vscode.Uri, type: MagicResourceType, groupPathSub: string | undefined, fileName: string, script: string, extraMeta?: Partial<MirrorFileMeta>) => Promise<void>,
    ): void {
        this.deps.readMirrorMeta = readMirrorMeta;
        this.deps.writeMirrorMeta = writeMirrorMeta;
        this.deps.toMirrorMetaFromServer = toMirrorMetaFromServer;
        this.deps.writeLocalMeta = writeLocalMeta;
        this.deps.writeLocalGroupMeta = writeLocalGroupMeta;
        this.deps.writeLocalScriptAndUpdateMeta = writeLocalScriptAndUpdateMeta;
    }

    // 通过 groupPathSub 逐级创建缺失的分组（保留供后续 createFile 使用）
    public async ensureGroups(client: MagicApiClient, type: MagicResourceType, groupPathSub: string | undefined): Promise<void> {
        if (!groupPathSub) return;
        const segments = groupPathSub.split('/').filter(Boolean);
        await client.getResourceDirs();
        let parentId: string | null = null;
        const acc: string[] = [];
        for (const seg of segments) {
            acc.push(seg);
            const pathKey = `${type}/${acc.join('/')}`;
            const gid = client.getGroupIdByPath(pathKey);
            if (!gid) {
                const newId: string | null = await client.createGroup({ name: seg, parentId, type });
                await client.getResourceDirs();
                parentId = newId;
            } else {
                parentId = gid;
            }
        }
    }

    // 收集全部资源中的文件，拼接下载参数（仅文件，排除 root/group）
    private async collectAllFileResources(client: MagicApiClient): Promise<Array<{ type: MagicResourceType; id: string }>> {
        const results: Array<{ type: MagicResourceType; id: string }> = [];
        const seenFile = new Set<string>();
        // 顶层类型目录：只收集文件，避免传递 root
        for (const t of MAGIC_RESOURCE_TYPES) {
            const files = await client.getFiles(t as MagicResourceType, null);
            for (const f of files) {
                if (!seenFile.has(f.id)) { results.push({ type: f.type as MagicResourceType, id: f.id }); seenFile.add(f.id); }
            }
        }
        // 收集分组目录内的文件，排除 group 目录本身
        const dirs = await client.getResourceDirs();
        for (const d of dirs) {
            const files = await client.getResourceFiles(d);
            for (const f of files) {
                if (!seenFile.has(f.id)) { results.push({ type: f.type as MagicResourceType, id: f.id }); seenFile.add(f.id); }
            }
        }
        return results;
    }

    // 使用 ZIP 从服务器同步到本地镜像根目录
    public async syncFromServer(
        client: MagicApiClient,
        root: vscode.Uri,
        serverConfig?: MagicServerConfig,
        progress?: vscode.Progress<{ message?: string; increment?: number }>
    ): Promise<void> {
        progress?.report({ message: '初始化镜像目录结构' });
        await this.ensureDir(root);
        for (const t of MAGIC_RESOURCE_TYPES) {
            await this.ensureDir(vscode.Uri.joinPath(root, t));
        }

        // 在镜像根目录释放 AGENTS.md（若不存在）
        try {
            const agentsUri = vscode.Uri.joinPath(root, 'AGENTS.md');
            let exists = true;
            try { await vscode.workspace.fs.stat(agentsUri); } catch { exists = false; }
            if (!exists) {
                const content = getAgentsManual();
                await vscode.workspace.fs.writeFile(agentsUri, Buffer.from(content, 'utf8'));
            }
        } catch {}

        // 收集资源并下载 ZIP -> 作为 yield 暴露给上层
        progress?.report({ message: '收集资源列表' });
        let written = 0;
        const inflight: Promise<void>[] = [];
        // 大幅提高并发限制，充分利用异步 I/O
        const limit = 64;
        const createdDirs = new Set<string>();
        const ensureOnce = async (uri: vscode.Uri) => {
            const key = uri.toString();
            if (createdDirs.has(key)) return;
            await this.ensureDir(uri);
            createdDirs.add(key);
        };
        // 优化 track：不阻塞调用者，仅在达到限制时等待
        const track = (p: Promise<void>) => {
            const wrapped = p.catch(err => {
                // 静默处理错误，避免中断整个同步流程
                console.error('[Mirror Sync] Write error:', err);
            }).finally(() => {
                const idx = inflight.indexOf(wrapped);
                if (idx >= 0) inflight.splice(idx, 1);
            });
            inflight.push(wrapped);
            // 达到限制时，等待最快完成的任务
            if (inflight.length >= limit) {
                return Promise.race(inflight).catch(() => {});
            }
            return Promise.resolve();
        };

        for await (const item of this.downloadAllResourcesYield(client, progress)) {
            if (item.kind === 'script') {
                // 不等待 track 返回，直接启动下一个任务
                await track((async () => {
                    const converted = this.deps.toMirrorMetaFromServer(item.meta);
                    // 若服务器树中已有 fullUrlPath，直接透传到 autoGeneration，避免依赖本地 .group-meta.json
                    try {
                        const fid = String((item.meta as any)?.id || '');
                        let fullUrl: string | undefined;
                        if (fid) {
                            try { fullUrl = await client.getFullUrlPathById(fid); } catch {}
                        }
                        const extra: any = (converted as any).extra || {};
                        const ag: any = extra.autoGeneration || {};
                        if (fullUrl && typeof fullUrl === 'string') {
                            ag.fullUrlPath = fullUrl;
                        }
                        if (Object.keys(ag).length > 0) {
                            extra.autoGeneration = ag;
                            (converted as any).extra = extra;
                        }
                    } catch {}
                    await this.deps.writeLocalScriptAndUpdateMeta(
                        root,
                        item.type,
                        item.groupPathSub,
                        item.fileName,
                        item.script,
                        { ...converted }
                    );
                    written++;
                    progress?.report?.({ message: `写入脚本 ${item.type}${item.groupPathSub ? '/' + item.groupPathSub : ''}/${item.fileName}.ms` });
                })());
            } else if (item.kind === 'group-meta') {
                await track((async () => {
                    try {
                        const gm = item.parsed ?? (JSON.parse(item.rawText) as MagicGroupMetaRaw);
                        // 跳过递归刷新，避免全量写入带来的性能问题；文件写入时会计算 fullUrlPath
                        await this.deps.writeLocalGroupMeta(root, item.type, item.groupPathSub, gm, { refreshChildren: false });
                    } catch {
                        const dirUri = vscode.Uri.joinPath(root, item.type, ...(item.groupPathSub ? item.groupPathSub.split('/') : []));
                        await ensureOnce(dirUri);
                        // 回退：仍按本地约定写入 .group-meta.json
                        const targetUri = vscode.Uri.joinPath(dirUri, '.group-meta.json');
                        await vscode.workspace.fs.writeFile(targetUri, Buffer.from(item.rawText, 'utf8'));
                    }
                    written++;
                    progress?.report?.({ message: `写入分组元数据 ${item.type}${item.groupPathSub ? '/' + item.groupPathSub : ''}/.group-meta.json` });
                })());
            } else {
                await track((async () => {
                    const parts = item.relPath.split('/').filter(Boolean);
                    const file = parts.pop() || '';
                    const dirUri = vscode.Uri.joinPath(root, ...parts);
                    await ensureOnce(dirUri);
                    const targetUri = vscode.Uri.joinPath(dirUri, file);
                    await vscode.workspace.fs.writeFile(targetUri, Buffer.from(item.data));
                    written++;
                    progress?.report?.({ message: `写入 ${item.relPath}` });
                })());
            }
        }
        // 等待所有剩余任务完成
        await Promise.all(inflight);
        progress?.report({ message: `ZIP 写入完成（${written} 项）` });

        // 追加：遍历所有分组目录，确保本地存在 .group-meta.json（用于新建文件自动填充 groupId）
        try {
            const dirs = await client.getResourceDirs();
            const inflightGroups: Promise<void>[] = [];
            const gLimit = 32;
            const gTrack = (p: Promise<void>) => {
                const wrapped = p.catch(err => {
                    console.error('[Mirror Sync] Write group meta error:', err);
                }).finally(() => {
                    const idx = inflightGroups.indexOf(wrapped);
                    if (idx >= 0) inflightGroups.splice(idx, 1);
                });
                inflightGroups.push(wrapped);
                if (inflightGroups.length >= gLimit) {
                    return Promise.race(inflightGroups).catch(() => {});
                }
                return Promise.resolve();
            };

            for (const dir of dirs) {
                await gTrack((async () => {
                    const segs = dir.split('/').filter(Boolean);
                    const type = (segs[0] || '') as MagicResourceType;
                    if (!type || !(MAGIC_RESOURCE_TYPES as readonly string[]).includes(type)) return;
                    const groupPathSub = segs.slice(1).join('/');
                    const gm = await client.getGroupMetaByDir(dir);
                    if (gm) {
                        await this.deps.writeLocalGroupMeta(root, type, groupPathSub, gm, { refreshChildren: false });
                        progress?.report?.({ message: `写入分组元数据 ${type}${groupPathSub ? '/' + groupPathSub : ''}/.group-meta.json` });
                    }
                })());
            }
            await Promise.all(inflightGroups);
        } catch {}

        // 写入镜像连接信息
        const mirrorMeta: MirrorMeta = {
            createdAt: Date.now(),
            url: serverConfig?.url,
            token: serverConfig?.token,
            username: serverConfig?.username,
            password: serverConfig?.password,
            lspPort: (serverConfig as any)?.lspPort,
            debugPort: (serverConfig as any)?.debugPort,
        };
        await this.deps.writeMirrorMeta(root, mirrorMeta);
    }

    // 新增：下载全部资源并以 ZIP 条目迭代器形式返回（供批量 diff 复用）
    public async *downloadAllResourcesYield(client: MagicApiClient, progress?: vscode.Progress<{ message?: string; increment?: number }>): AsyncGenerator<ZipYieldItem> {
        progress?.report?.({ message: '下载并解析镜像 ZIP' });
        const resources = await this.collectAllFileResources(client);
        const files = resources.length;
        debug(`mirror payload: files=${files}`);
        const zip = await client.downloadMirrorZip({ resources });
        for await (const item of iterateZipEntries(zip)) {
            yield item;
        }
    }

    // 刷新写入工作台补全数据（classes/extensions/functions）到镜像元数据
    public async refreshWorkbenchCompletionData(client: MagicApiClient, root: vscode.Uri, progress?: vscode.Progress<{ message?: string; increment?: number }>): Promise<void> {
        try {
            const data = await client.getWorkbenchCompletionData();
            if (!data) return;
            const meta = (await this.deps.readMirrorMeta(root)) || { createdAt: Date.now() } as MirrorMeta;
            meta.workbench = {
                ...(meta.workbench || {}),
                classes: (data?.classes ?? data?.classMap ?? data?.classesMap ?? data?.classes) ?? undefined,
                extensions: (data?.extensions ?? data?.extensionClasses ?? data?.extensionMap) ?? undefined,
                functions: (Array.isArray(data?.functions) ? data?.functions : (data?.funcs ?? data?.functionsList)) ?? undefined,
                lastUpdated: Date.now(),
            };
            await this.deps.writeMirrorMeta(root, meta);
            progress?.report?.({ message: '写入工作台补全数据' });
        } catch {}
    }
}